#include "subwidget1.h"
#include "ui_subwidget1.h"
#include "subwidget_serial.h"
#include "mythreadforsubwidget1.h"
#include <QSerialPort>
#include <QLabel>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonObject>
class SubWidget2;


SubWidget1::SubWidget1(QSqlDatabase &db, SubWidget_Serial &Serial, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SubWidget1),
    m_Serial(&Serial),
    m_db(db)               //由于我们是在private中最后一个将m_db声明的，所以建议按照顺序初始化，具体可参考博客1020和1027(重点1020)
{
    ui->setupUi(this);
    thread_w1 = new MyThreadForSubwidget1(this);
    thread_w1->start();
    thread_w1->wait();
    setWindowTitle("实时监测窗口");

    m_speed1 = 50;
    //电机速度
    ui->label_speed1->setText(QString("speed1:%1 m/s").arg(m_speed1));        //当 label中的内容显示不全时，可以通过在ui设计界面中，将图框拉大，来实现
    ui->label_speed2->setText(QString("speed2:%1 m/s").arg(m_speed2));
    //温度传感器
    ui->label_T1->setText(QString("T1:10'c"));
    ui->label_T2->setText(QString("T2:10'c"));
    ui->label_T3->setText(QString("T3:10'c"));
    ui->label_T4->setText(QString("T4:10'c"));
    ui->label_T5->setText(QString("T5:10'c"));
    ui->label_T6->setText(QString("T6:11'c"));
    //湿度传感器
    ui->label_RH1->setText(QString("RH1:10"));
    ui->label_RH2->setText(QString("RH2:10"));
    ui->label_RH3->setText(QString("RH3:10"));
    ui->label_RH4->setText(QString("RH4:10"));
    ui->label_RH5->setText(QString("RH5:10"));
    //炉温
    ui->label_T_furnace->setText(QString("炉温:100'C"));

    m_sub2 = new SubWidget2(m_db);                //借用一下我们在sub2中写的代码,临时用来获取数据，后续不能用1来获取数据
    m_sub2->moveDataBase_to_table();            //这是为了，让容器内有数据

    QVector<int>v1;
    v1.push_back(e_t_wind1);
    v1.push_back(e_t_wind2);
    v1.push_back(e_t_wind3);
    v1.push_back(e_t_tower1);
    v1.push_back(e_t_tower2);
    v1.push_back(e_t_tower3);
    v1.push_back(e_t_furnace);
    QVector<int>v2;
    v2.push_back(e_v_coal);
    v2.push_back(e_v_grain);
    QVector<int>v3;
    v3.push_back(1);
    //初始化两个图表的格式
    chart_init(ui->widget_chart1, v1, 700, "数据库数据-温度");
    chart_init(ui->widget_chart2, v2, 100, "数据库数据-速度");
    chart_init(ui->widget_chart3, v3, 100, "传感器实测数据-湿度");

    double orignt_time = QDateTime::currentDateTime().toTime_t();
    //创建定时器，用于定时生成曲线坐标点数据
    QTimer *timer = new QTimer(this);
    timer->start(1000);           //设置触发频率？0.1s触发一次？
    //connect(timer, SIGNAL(timeout()), this, [=](){this->TimeData_Update(ui->widget_chart1, v1);});   //每过10s，触发一下，往图表里面添加一个数据
    connect(timer, &QTimer::timeout, this, [=](){
        TimeData_Update(ui->widget_chart1, v1, orignt_time);
        TimeData_Update(ui->widget_chart2, v2, orignt_time);
        });


//    //串口部分            这一部分本来就是串口负责的，所以让他们自己鼓捣最合适
//    //通过串口发送数据，从这里往里面写入协议
//    connect(timer, &QTimer::timeout, this,
//            [=](){
//                     if(m_Serial->is_SerialPort_ready())
//                     {
//                         qDebug()<<"发送数据。。。";
//                         char arr[] = {0x01, 0x03, 0x00, 0x12, 0x00, 0x01, 0x24, 0x0F};
//                         QByteArray data_arr;
//                         for(char temp:arr)
//                         {
//                             data_arr.append(temp);
//                         }
//                         qDebug()<<data_arr;
//                          m_Serial->send_message(data_arr);
//                     }
//                 });
//    //这里用get_mySerialPort来获取私有属性的引用，不知道合不合适
//    connect(&(m_Serial->get_mySerialPort()), &QSerialPort::readyRead, this,
//            [=](){
//                     double data = m_Serial->receive_message();
//                     TimeData_Update(ui->widget_chart3, v3, orignt_time, data, true);
//                 }
//            );

    /*//读取Json文件，获得10进制数据，将其转换为16进制
    //read_json(":/new/prefix1/motor.json", "test1");
    QByteArray arr_speed = read_json("./json_rc/motor.json", "set_speed");
    QByteArray arr_start = read_json("./json_rc/motor.json", "start");
    QByteArray arr_stop = read_json("./json_rc/motor.json", "stop");
    QByteArray arr_sensor_th1 = read_json("./json_rc/sensor.json", "sensor_th1");
    qDebug()<<"arr_speed  "<<arr_speed.toHex();
    qDebug()<<"arr_start  "<<arr_start.toHex();
    qDebug()<<"arr_stop  "<<arr_stop.toHex();
    qDebug()<<"arr_sensor_th1"<<arr_sensor_th1.toHex();*/     //超过一定数值之后，会根据ASCII对应的字母 打印出来结果，如100虽然可以以0x64显示，但系统会以"b"的形式显示char型字符
}

/**
 * @brief SubWidget1::chart_init 初始化目标图表的格式
 * @param customPlot 当前表的指针
 * @param v 几个测试用数据的容器的索引
 */
void SubWidget1::chart_init(QCustomPlot *customPlot,const QVector<int> &v, double upper_yAxis, QString title)
{
    //关于图表格式的设置
    QPen pen;               //创建画笔
    pen.setWidth(2);        //设置笔的粗细
    for(QVector<int>::const_iterator it= v.begin(); it!=v.end(); it++)
    {
        customPlot->addGraph();
        pen.setColor(QColor(qSin((*it)*1+1.2)*80+80, qSin((*it)*0.3+0)*80+80, qSin((*it)*0.3+1.5)*80+80));
        customPlot->graph()->setPen(pen);                       // 设置画笔
        customPlot->graph()->setName(m_sub2->m_lineNames.at((*it)-1));        //图例名称
        customPlot->legend->setVisible(true);       // 显示图例
    }
    //customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    customPlot->setInteractions(QCP::iRangeZoom | QCP::iSelectPlottables);
    customPlot->xAxis->setLabel(title);
    customPlot->yAxis->setRange(0, upper_yAxis);  //纵轴的范围还是得调整

    //这里是设置日期的显示格式
    QSharedPointer<QCPAxisTickerDateTime> xTicker(new QCPAxisTickerDateTime);
    xTicker->setTickCount(10);                                          //设置横坐标的个数
    xTicker->setDateTimeFormat("yy-MM-dd hh:mm:ss");//
    customPlot->xAxis->setTicker(xTicker);
    customPlot->xAxis->setTickLabelRotation(30);   //设置刻度标签顺时针旋转30度   这个不需要在每个地方都设置一遍，只要有一个图表设置了，其他的都会有，但防止用户先显示另一个表，导致这个格式未设置上，所以在这里都设置了一下

}

/**
 * @brief SubWidget1::TimeData_Update 触发此函数用来给plot_chart()函数传入其所需的几个数据
 * @param customPlot 当前表的指针
 * @param v 几个测试用数据的容器的索引
 * @param orignt_time 最开始的时间戳
 * @param data 默认不传入数据，所以设置数据为0，但是我们测量出来的数据可能为0
 * @param is_write 因为传入的数据可能是0，所以不能通过data是否为0判断我们有没有写入数据，所以单独弄了write这个参数
 */
void SubWidget1::TimeData_Update(QCustomPlot *customPlot,const QVector<int>& v, double orignt_time, double data, bool is_write)
{
    static float f;
    if(!is_write)
    {
        f += 1;
    }

    double cnt2;
    cnt2 = QDateTime::currentDateTime().toTime_t();
    double cnt3 = QTime::currentTime().minute()*60+QTime::currentTime().second();

    //if(f>=(m_sub2->m_v_date.size()-1))
    if(f>=520)
    {
        qDebug()<<"SubWidget1::TimeData_Update ： 数据显示完成。。。"<<f<<"   "<<m_sub2->m_v_date.size();
        return ;
    }
    // qDebug()<<"SubWidget1::TimeData_Update ： 数据大小。。。"<<f<<"   "<<m_sub2->m_v_date.size()<<"   "<<m_sub2->m_v_t_tower1.size();
    //这里将每个曲线的数据更新进去一次，后续，从下位机获取数据后，就从这里一个一个添加到图中
    int i=0;
    for(QVector<int>::const_iterator it=v.begin(); it!=v.end(); it++)
    {
        //判断未从外界写入数据时
        if(!is_write)
        {
            data = m_sub2->getSequence(*it)[f];     //如果判断没写入数据，则我们传入就将容器内的数据填入m_sub2->getSequence(*it)[f]
            set_Qlabel(*it, data);          //暂时写在这里，因为暂时使用数据库里的数据来显示
            m_sub2->getSequence(*it).pop_front();
        }
        this->plot_chart(customPlot->graph(i) , data, cnt2);                        //这里后续填上cnt2
        i++;
        //实时动态图，画图不显示当前时刻的数据，只显示1970年0点0刻开始的原因，是数据早就画在图上了，只是我们通过setRange显示数据的范围一直是从0至100，所以在图上一直看不到
        //customPlot->xAxis->setRange((pGraph1_1->dataCount()>1000)?(pGraph1_1->dataCount()-1000):0, pGraph1_1->dataCount());
        customPlot->xAxis->setRange((customPlot->graph()->dataCount()>30)?(cnt2-30):orignt_time, cnt2);
    }
    // 更新绘图，这种方式在高填充下太浪费资源。有另一种方式rpQueuedReplot，可避免重复绘图。
    // 最好的方法还是将数据填充、和更新绘图分隔开。将更新绘图单独用定时器更新。例程数据量较少没用单独定时器更新，实际工程中建议大家加上。
    //customPlot->replot();
    customPlot->replot(QCustomPlot::rpQueuedReplot);
}

/**
 * @brief SubWidget1::plot_chart 画图
 * @param graph 折线
 * @param data
 * @param cnt2
 */
void SubWidget1::plot_chart(QCPGraph *graph, double data, double cnt2)
{
    if(!cnt2)
    {
        static double cnt;
        //qDebug()<<"这里是SubWidget1"<<graph->name()<<data<<QDateTime::currentDateTime().toTime_t()<<"cnt:"<<cnt;
        cnt++;
        graph->addData(cnt, data);
        return ;
    }
    //qDebug()<<"这里是SubWidget1"<<graph->name()<<data<<QDateTime::currentDateTime().toTime_t();
    graph->addData(cnt2, data);

}

/**
 * @brief SubWidget1::set_Qlabel 设置当前text的标签
 * @param index 索引当前的text
 * @param data 所要显示的数据
 */
void SubWidget1::set_Qlabel(int index, double data)
{
    switch (index)
    {
    case e_t_furnace:
        ui->label_T_furnace->setText(QString("T:%1 ’C").arg(data));
        break;
    case e_t_wind1:
        ui->label_T1->setText(QString("T:%1‘C").arg(data));
        break;
    case e_t_wind2:
        ui->label_T2->setText(QString("T:%1‘C").arg(data));
        break;
    case e_t_wind3:
        ui->label_T3->setText(QString("T:%1‘C").arg(data));
        break;
    case e_t_tower1:
        ui->label_T4->setText(QString("T:%1‘C").arg(data));
        break;
    case e_t_tower2:
        ui->label_T5->setText(QString("T:%1‘C").arg(data));
        break;
    case e_t_tower3:
        ui->label_T6->setText(QString("T:%1‘C").arg(data));
        break;
    case e_v_coal:
        ui->label_speed1->setText(QString("speed1:%1 m/s").arg(data));
        break;
    case e_v_grain:
        ui->label_speed2->setText(QString("speed2:%1 m/s").arg(data));
        break;
    case e_rh_after_grain:
        ui->label_RH1->setText(QString("RH:%1").arg(data));
        break;
    case e_rh_before_grain:
        ui->label_RH2->setText(QString("RH:%1").arg(data));
        break;
    default:
        qDebug()<<"d="<< data <<"向容器插入数据失败\n";
    break;
    }
}

/**
 * @brief SubWidget1::get_Ui 返回对当前对象Ui，方便外界调用时用
 * @return ui
 */
const Ui::SubWidget1& SubWidget1::get_Ui() const
{
    return *ui;
}

/**
 * @brief SubWidget1::closeEvent 重写关闭窗口事件
 * @param event 我们不想让窗口被这个子窗口关闭，所以忽略这个子窗口触发的关闭事件
 */
void SubWidget1::closeEvent(QCloseEvent *event)
{
    qDebug()<<"尝试关闭窗口1";
//    this->setWindowFlags(Qt::WindowMinimizeButtonHint);
//    event->accept();    //接收关闭窗口事件
//    event->ignore();    //忽略关闭窗口的事件，因为 我们不想让创建的对象关闭
}

/**
 * @brief SubWidget1::read_json
 * @param path
 * @param key
 */
QByteArray SubWidget1::read_json(QString path, QString key)
{
    QFile file(path);
    if(!file.open(QIODevice::ReadOnly))             //这里没有写文件，都是读文件内容，所以不需要设置线程锁来保证每次只有一个线程对其进行操作
    {
        qDebug()<<"打开失败！";
        exit(1);                //终止程序
    }
    qDebug()<<"打开成功！";
    QJsonParseError jsonParserError;
    QJsonDocument jsonDecument = QJsonDocument::fromJson(file.readAll(), &jsonParserError);
    file.close();
    qDebug()<<"1111111:   "<<jsonParserError.error<< "  "<<jsonDecument.isNull();
    if(!jsonDecument.isNull() && jsonParserError.error==QJsonParseError::NoError)       //判断读取的内容是否有误
    {
        qDebug()<<"文件解析成功";
        if(jsonDecument.isObject())             //这里明天再看看，博客说里面封装了Json中的对象
        {
            QJsonObject jsonObject = jsonDecument.object();
//            if(jsonObject.contains(key))                                  //尝试读取数值
//            {
//                qDebug()<<key<<"  :  "<<jsonObject.value(key);
//            }
            if(jsonObject.contains(key) && jsonObject.value(key).isArray()) //读取数组
            {
                QJsonArray jsonArray = jsonObject.value(key).toArray();
                QByteArray data_arr;
                //unsigned char *readyToCTC;
                unsigned char readyToCTC[jsonArray.size()];
                for(int i=0; i<jsonArray.size(); i++)
                {
//                    qDebug()<<jsonArray[i].toInt();
                    //QString qnum = QString::number(jsonArray[i].toInt(), 16).right(2);
                    QString qnum = "0x"+QString("%1").arg(jsonArray[i].toInt(), 2, 16, QLatin1Char('0'));  // 保留两位，不足补零
                    unsigned char buff = (unsigned char)(jsonArray[i].toInt());
                    data_arr.append(buff);
                    readyToCTC[i] = buff;
                    //qDebug()<<data_arr;
                }
                unsigned int crc_value = crc_cjl_value(readyToCTC, jsonArray.size());       //unsigned int貌似就两个字节
                //数据帧中，校验码顺序是低位在前，高位在后
                unsigned char low_crc = (unsigned char)crc_value;
                unsigned char high_crc = (unsigned char)(crc_value>>8);              //不知道为啥，这里也许要加上unsigned，否则结果不对
                qDebug()<<" crc_value    "<<crc_value<< " low_crc  "<<low_crc<<" high_crc   "<<high_crc;
                data_arr.append(low_crc);
                data_arr.append(high_crc);       //超过一定数值之后，会根据ASCII对应的字母 打印出来结果，如100虽然可以以0x64显示，但系统会以"b"的形式显示char型字符
                return data_arr;
            }
        }
    }
    QMessageBox::warning(this, "json文件异常", "读取json文件的协议内容失败，请检查json文件", QMessageBox::Button::Ok);
    return NULL;
}

/**
 * @brief SubWidget1::crc_cjl_value 计算crc用
 * @param data_value        这里的指针就是代指的传数组名而已
 * @param length
 * @return
 */
unsigned int SubWidget1::crc_cjl_value(unsigned char *data_value, unsigned char length)
{
    unsigned int crc_value = 0xffff;
    int i;
    while(length--)
    {
        crc_value^=*data_value++;
        for(i=0; i<8; i++)
        {
            if(crc_value&0x0001)
            {
                crc_value = (crc_value>>1)^0xa001;
            }
            else
            {
                crc_value = (crc_value>>1);
            }
        }
    }
    return crc_value;
}

void SubWidget1::test_to_use_TimeData_Update(int index, double data)
{
    double orignt_time = QDateTime::currentDateTime().toTime_t();
    QVector<int>v3;
    v3.push_back(index);
    TimeData_Update(ui->widget_chart3, v3, orignt_time, data, true);
    ui->widget_chart3->replot();
    qDebug()<<"subwidget1:发给数据库数据：  "<<index<<"  "<<data;
}

SubWidget1::~SubWidget1()
{
    delete ui;
    delete m_sub2;      //因为m_sub2是new出来的对象，而不是外界传进来的
}
